package com.wang.demo.protocol.codec;

import com.wang.demo.protocol.message.Message;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.List;

/**
 * <p>线程安全无状态编解码器
 * <p>协议类型为：
 * <p><table>
 *     <tr>
 *         <th>魔数</th>
 *         <th>版本号</th>
 *         <th>序列化方式</th>
 *         <th>指令类型</th>
 *         <th>序列化id</th>
 *         <th>对齐填充</th>
 *     </tr>
 *     <tr>
 *         <th>4byte</th>
 *         <th>1byte</th>
 *         <th>1byte</th>
 *         <th>1byte</th>
 *         <th>4byte</th>
 *         <th>1byte</th>
 *     </tr>
 *     <tr>
 *         <th>1,2,3,4</th>
 *         <th>1</th>
 *         <th>0jdk 1json</th>
 *         <th>{@link com.wang.demo.protocol.message.Message}</th>
 *         <th>请求连接序号</th>
 *         <th>0xff</th>
 *     </tr>
 * </table>
 *
 * <p>
 *     <ul>
 *         <li>MessageToMessageCodec</li>
 *         <ul>
 *             <li>认为第一次拿到的消息就已经是完整的消息了，不存在半包黏包的状态</li>
 *             <li>必须和LengthFieldBasedFrameDecoder 一起使用，确认接到的Msg消息是完整的</li>
 *         </ul>
 *     </ul>
 *     <ul>
 *         <li>ByteToMessageCodec</li>
 *         <ul><li>则是默认是在编解码器内部需要考虑半包黏包问题，需要在内部解决</li></ul>
 *     </ul>
 * @author: Jeffrey
 * @date: 2022/01/18/8:37
 * @description:
 */
@ChannelHandler.Sharable
public class MessageCodecSharable extends MessageToMessageCodec<ByteBuf, Message> {


    /**
     * 编码器
     * @param ctx 流水线上下文
     * @param msg 消息
     * @param outList 输出
     * @throws Exception
     */
    @Override
    protected void encode(ChannelHandlerContext ctx, Message msg, List<Object> outList) throws Exception {
        /*这里需要自己创建ByteBuf*/
        ByteBuf out = ctx.alloc().buffer();
        /*1. 魔数 4Byte*/
        out.writeBytes(new byte[]{1,2,3,4});
        /*2. 版本号 1Byte*/
        out.writeByte(1);
        /*3. 序列化方式 jdk 0 json 1  1byte*/
        out.writeByte(0);
        /*4. 指令类型 1byte*/
        out.writeByte(msg.getMessageType());
        /*5. 请求序号 4byte*/
        out.writeInt(msg.getSequenceId());
        /*---------------首部有效部分 11Byte--------------------*/
        /*6. 无效填充，对齐 1byte*/
        out.writeByte(0xff);

        ByteArrayOutputStream bos= new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(msg);
        byte[] bytes = bos.toByteArray();

        /*7. 数据长度，4byte*/
        out.writeInt(bytes.length);
        /*8. 写入数据*/
        out.writeBytes(bytes);

        /*数据传入下一个处理器*/
        outList.add(out);
    }

    /**
     *
     * 解码器
     * @param ctx 流水线上下文
     * @param in 这里的ByteBuf 不会有线程安全的问题，因为我们可以确定，
     *           这里自定义编解码器的上一个一定是帧解码器，帧解码器可以将
     *           一个完整的ByteBuf 传递给这个自定义编解码器，所以这里就不用
     *           去考虑状态
     * @param out 输出
     * @throws Exception
     */
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        int magicNum = in.readInt();
        byte version = in.readByte();
        byte serializerType = in.readByte();
        byte messageType = in.readByte();
        int sequenceId =in.readInt();
        in.readByte();
        int length = in.readInt();
        byte[] bytes = new byte[length];
        in.readBytes(bytes,0,length);
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
        Message message = (Message) ois.readObject();
        out.add(message);
    }
}
